﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.IO;

namespace SimonPG {

    public partial class ExceptionForm : Form {

        StackTraceAnalyzer _Analyzer;

        public ExceptionForm() {
            InitializeComponent();
        }

        public ExceptionForm(Exception ex)
            : this() {

            _UIClassName.Text = string.Format("例外: {{{0}}}", ex.GetType().Name);
            _UIExplanation.Text = string.Format("説明: {0}", ex.Message);
            _Analyzer = new StackTraceAnalyzer(ex);
            foreach (var stack in _Analyzer.Stacks) {

                var item = new ListViewItem(new[] { stack.MethodName, stack.Namespace });
                if (!stack.SourceCode) {
                    item.ForeColor = Color.Gray;
                }
                item.Tag = stack;
                _UIStackList.Items.Add(item);
            }
            _UIStackList_Name.Text = string.Format("メソッド名 [{0}]", _Analyzer.Stacks.Length);
        }

        private void _UIClose_Click(object sender, EventArgs e) {
            this.Close();
        }

        private void _UIStackList_SelectedIndexChanged(object sender, EventArgs e) {

            var stack = _UIStackList.FocusedItem.Tag as StackTraceAnalyzer.Stack;

            var text = string.Format(
@"メソッド名:
- {0}{2}
名前空間:
- {1}
", stack.MethodName, stack.Namespace, stack.Arguments);

            if (stack.SourceCode) {
                text += string.Format(
@"行番号:
- {0}
ソースファイル:
- {1}
", stack.LineNumber, stack.Location);

            } else {
                text +=
@"行番号:
- 不明
ソースファイル:
- 外部のソースコード";
            }

            _UIStackDetail.Text = text;
        }

        private void _UISaveLog_Click(object sender, EventArgs e) {

            using (var file = File.CreateText("exception.log")) {

                file.WriteLine(
@"date:
- {0}
exception class name:
- {1}
exception explanation:
- {2}
",
                    DateTime.Now, _Analyzer.Exception.GetType().Name,
                    _Analyzer.Exception.Message);

                file.WriteLine("stack trace: [{0}]", _Analyzer.Stacks.Length);
                foreach (var stack in _Analyzer.Stacks) {

                    file.WriteLine(@"
    method name:
    - {0}
    arguments:
    - {1}
    namespace:
    - {2}",
                        stack.MethodName, stack.Arguments, stack.Namespace);

                    if (stack.SourceCode) {
                        file.WriteLine(
@"    source code:
    - internal
    code location:
    - {0}
    line number:
    - {1}",
                            stack.Location, stack.LineNumber);
                    } else {
                        file.WriteLine(
@"    source code:
    - external");
                    }
                }
            }

            MessageBox.Show(this, "エラーの全ログを保存しました。");
        }
    }

    public class StackTraceAnalyzer {

        public readonly Exception Exception;
        public readonly Stack[] Stacks;

        public StackTraceAnalyzer(Exception ex) {

            var re_name = new Regex(@"^   [^ ]+ (?<s>.+?)\.(?<n>[^\.]+?)(?<a>\(.*?\))(?<2>.*)$", RegexOptions.Multiline);
            var re_loca = new Regex(@"^ [^ ]+ (?<a>.+):[^ ]+ (?<l>[0-9]+).*?$");
            var matches = re_name.Matches(ex.StackTrace);
            var stacks = new List<Stack>();
            foreach (Match matched in matches) {

                var stack = new Stack();
                stack.Namespace = matched.Groups["s"].Value;
                stack.MethodName = matched.Groups["n"].Value;
                stack.Arguments = matched.Groups["a"].Value;
                var line_second = matched.Groups["2"].Value;

                var matched_s = re_loca.Match(line_second);
                if (matched_s.Success) {
                    stack.SourceCode = true;
                    stack.Location = matched_s.Groups["a"].Value;
                    stack.LineNumber = int.Parse(matched_s.Groups["l"].Value);
                }
                stacks.Add(stack);
            }

            this.Exception = ex;
            this.Stacks = stacks.ToArray();
        }

        public class Stack {
            public string MethodName;
            public string Namespace;
            public bool SourceCode;
            public string Arguments;
            public string Location;
            public int LineNumber;
        }
    }
}
